<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
		"http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<title>doh.robot Spinner Test</title>

		<style>
			@import "../../../../util/doh/robot/robot.css";
		</style>

		<!-- required: dojo.js -->
		<script type="text/javascript" src="../../../../dojo/dojo.js"
			djConfig="isDebug: true, parseOnLoad: true"></script>

		<script type="text/javascript">
			dojo.require("dijit.dijit"); // optimize: load dijit layer
			dojo.require("dijit.robotx");

			dojo.addOnLoad(function(){
				var spin1;
				var spin2;
				var spin3;
				var spinnerIds = ["integerspinner1", "integerspinner2", "integertextbox3", "realspinner1"];
				doh.robot.initRobot('../test_Spinner.html');
				
				// focus handlers for noticing tab/shift+tab navigation
				var focusCountZ = 0;
				var blurCountZ = 0;
				var countFocus = function(){
					focusCountZ++;
				}
				var countBlur = function(){
					blurCountZ++;
				}
				var focusConnect;
				var blurConnect;
				var tabFocusSetup = function(inSpinnerId){
					var spinner = dijit.byId(inSpinnerId);
					focusCountZ = 0;
					blurCountZ = 0;
					focusConnect = spinner.connect(spinner.focusNode, "onfocus", countFocus);
					blurConnect = spinner.connect(spinner.focusNode, "onblur", countBlur);
				}
				
				// Setup to test manipulation of spinner via keystrokes.
				var results;	// array holding both expected value and actual value after each keypress
				var strokeIndex = 0;
				var noteConnect;
				function noteChanges(){
					results[strokeIndex].actual = spinner.attr('value');
					strokeIndex++;
				}
				function keyStrokeSetup(inSpinnerId){
					// summary:
					//		Populates results[] with expected values after each keypress.
					//		Also sets up listener to every keyboard event (arrow key etc) on a spinner and
					//		records to record the actual spinner values into results[].
					var spinner = dijit.byId(inSpinnerId);
					results = populateExpected(spinner);
					strokeIndex = 0;
					noteConnect = spinner.connect(spinner.focusNode, "onkeypress", function(){
						results[strokeIndex].actual = spinner.attr('value');
						strokeIndex++;
					});
				}

				// Use spinner api to calculate values for a set of keystrokes.
				// Returns array of expected values.
				var populateExpected = function(/*Spinner Widget*/inSpinner){
					var initVal = inSpinner.attr('value');
					var eVals = [];
					var newVal;
					
					// The values expected by pressing HOME, right arrow five times,
					// PgUp twice, PgDn, up arrow twice, down arrow once,
					// left arrow once, and END.
					if(inSpinner.constraints.min){
						inSpinner.attr('value', inSpinner.constraints.min);
						newVal = inSpinner.attr('value');
					}else{
						newVal = inSpinner.attr('value');
					}
					eVals.push({stroke: "HOME", expected: newVal});
					// UP 5 times
					for(var i = 0; i < 5; i++){
						newVal = inSpinner.adjust(newVal, inSpinner.smallDelta);
						inSpinner.attr('value', newVal);
						newVal = inSpinner.attr('value');
						eVals.push({stroke: "UP", expected: newVal});					
					}
					// PgUp twice
					for(var i = 0; i < 2; i++){
						newVal = inSpinner.adjust(newVal, inSpinner.largeDelta);
						inSpinner.attr('value', newVal);
						newVal = inSpinner.attr('value');
						eVals.push({stroke: "PgUp", expected: newVal});					
					}
					// PgDn once.
					newVal = inSpinner.adjust(newVal, -inSpinner.largeDelta);
					inSpinner.attr('value', newVal);
					newVal = inSpinner.attr('value');
					eVals.push({stroke: "PgDn", expected: newVal});					
					// DOWN
					newVal = inSpinner.adjust(newVal, -inSpinner.smallDelta);
					inSpinner.attr('value', newVal);
					newVal = inSpinner.attr('value');
					eVals.push({stroke: "DOWN", expected: newVal});					
					// END
					if(inSpinner.constraints.max){
						inSpinner.attr('value', inSpinner.constraints.max);
						newVal = inSpinner.attr('value');
					}else{
						newVal = inSpinner.attr('value');
					}
					eVals.push({stroke: "END", expected: newVal});
					
					// reset <inSpinner> back to its initial value, and return
					inSpinner.attr('value', initVal);
					return eVals;
				}
				
				// common robot TAB-focus test function.
				var a11yTabFocus = function(inSpinnerId){
					var d = new doh.Deferred();
					var spinner = dijit.byId(inSpinnerId);
					// insure a known focus starting point within document before key presses.
					doh.robot.sequence(dojo.hitch(spinner, "focus"), 500);
					// Shift-tab away, tab to, tab away, shift-tab back.
					doh.robot.keyPress(dojo.keys.TAB, 100, {shift:true});
					doh.robot.keyPress(dojo.keys.TAB, 100);
					doh.robot.keyPress(dojo.keys.TAB, 100);
					doh.robot.keyPress(dojo.keys.TAB, 100, {shift:true});
					
					// TODO: this doesn't seem thorough... after TAB or shift TAB
					// should be checking that we ended up at the right place, i.e.
					// make sure that we don't have extra tab stops within the Spinner itself.

					var checkGotFocus = function(){
						spinner.disconnect(focusConnect);
						spinner.disconnect(blurConnect);
						doh.assertEqual(3, focusCountZ, "# of times focused (" + spinner.id + ")");
						doh.assertEqual(2, blurCountZ, "# of times lost focus (" + spinner.id + ")");
					};
					doh.robot.sequence(d.getTestCallback(checkGotFocus), 1100);
					return d;
				}
				
				// common robot keystroke test function.
				var a11yKeystrokeTest = function(inSpinnerId){
					var d = new doh.Deferred();
					var spinner = dijit.byId(inSpinnerId);
					var initVal = spinner.attr('value');

					doh.robot.sequence(dojo.hitch(spinner, "focus"), 500);
					doh.robot.keyPress(dojo.keys.HOME, 100);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 100);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 100);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 100);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 100);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 100);
					doh.robot.keyPress(dojo.keys.PAGE_UP, 100);
					doh.robot.keyPress(dojo.keys.PAGE_UP, 100);
					doh.robot.keyPress(dojo.keys.PAGE_DOWN, 100);
					doh.robot.keyPress(dojo.keys.DOWN_ARROW, 100);
					doh.robot.keyPress(dojo.keys.END, 100);
					var testPresses = function(){
						spinner.disconnect(noteConnect);
						spinner.attr('value', initVal);
						for(var i = 0; i < results.length; i++){
							var aResult = results[i];
							if(!(isNaN(aResult.expected) && isNaN(aResult.actual))) {
								doh.is(aResult.expected, aResult.actual, aResult.stroke);
							}
						}
					};
					doh.robot.sequence(d.getTestCallback(testPresses), 1600);	// TODO: 1600 is the time after the END key is pressed, does it need to be that long?
					return d;
				}

				doh.register("setUp",{
					name: "setUp",
					timeout: 15000,
					setUp:function(){
						spin1=dijit.byId('integerspinner1');
						spin2=dijit.byId('integerspinner2');
						spin3=dijit.byId('realspinner1');
						safeClick=dojo.byId('form1');
					},
					runTest: function(){
						// assert onChange not fired
						doh.is("not fired yet!",dojo.byId('oc1').value);
						
						// make sure initial values are what we expect
						doh.is(1,spin1.smallDelta);
						doh.is(900, spin1.attr('value'), "integerspinner1");
						doh.is(1000, spin2.attr('value'), "integerspinner2");
						doh.is(1.0, spin3.attr('value'), "realspinner1");
					}
				});

				doh.register("a11y",{
					name: "spinner2_typematic",
					timeout: 15000,
					setUp:function(){
						spin2.attr('value', 900);
						spin2.focus();
					},
					runTest: function(){
						// test typematic
						var d=new doh.Deferred();
						doh.robot.keyDown(dojo.keys.DOWN_ARROW, 500);
						doh.robot.keyUp(dojo.keys.DOWN_ARROW, 5000);
						doh.robot.sequence(function(){
							if(spin2.attr('value')<=800){
								d.callback(true);
							}else{
								d.errback(new Error('Error in typematic test. Expected <=800, got '+spin2.attr('value')));
							}
						}, 1000);
						return d;
					}
				});

				doh.register("a11y",{
					name: "spinner2_max",
					timeout: 15000,
					setUp:function(){
						spin2.attr('value', 1549);
						spin2.focus();
					},
					runTest: function(){
						// test max with arrow key
						var d=new doh.Deferred();
						// press once: should move up
						doh.robot.keyPress(dojo.keys.UP_ARROW,500);
						doh.robot.sequence(d.getTestErrback(function(){
							doh.is(1550,spin2.attr('value'));
							doh.is("1550",spin2.focusNode.value);
							doh.is(true,spin2.isValid());
							// press again: shouldn't move
							doh.robot.keyPress(dojo.keys.UP_ARROW,500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.is(1550,spin2.attr('value'));
								doh.is("1550",spin2.focusNode.value);
								doh.is(true,spin2.isValid());
							}),500);
						}),500);

						return d;
					}
				});

				doh.register("a11y",{
					name: "spinner2_min",
					timeout: 15000,
					setUp:function(){
						spin2.attr('value', 10);
						spin2.focus();
					},
					runTest: function(){
						// test min with arrow key
						var d=new doh.Deferred();
						// press once: should move up
						doh.robot.keyPress(dojo.keys.DOWN_ARROW,500);
						doh.robot.sequence(d.getTestErrback(function(){
							doh.is(9,spin2.attr('value'));
							doh.is("9",spin2.focusNode.value);
							doh.is(true,spin2.isValid());
							// press again: shouldn't move
							doh.robot.keyPress(dojo.keys.DOWN_ARROW,500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.is(9,spin2.attr('value'));
								doh.is("9",spin2.focusNode.value);
								doh.is(true,spin2.isValid());
							}),500);
						}),500);

						return d;
					}
				});

				doh.register("a11y",{
					name: "spinner2_invalid",
					timeout: 15000,
					setUp:function(){
						spin2.focusNode.value="";
						spin2.focus();
					},
					runTest: function(){
						// assert invalid works
						var d=new doh.Deferred();
						doh.robot.typeKeys("0.5",500,300);
						doh.robot.sequence(d.getTestCallback(function(){
							doh.is(false,spin2.isValid());
						}),500);
						return d;
					}
				});
				
				// Test ARIA role.	Since role attribute is set in template,
				// checking one should be sufficient.
				doh.register("a11yAriaRole", 
					function spinnerRole(){
						var spinner = dijit.byId("integerspinner1");
						doh.isNot(spinner, null, "can't find 'integerspinner1'");
						doh.is(dijit.getWaiRole(spinner.focusNode), "spinbutton", spinner.id + ": aria role (spinbutton)");
					}
				);
				
				// Check ARIA min/max values.
				// - both min and max.
				// - min, no max.
				// - max, no min.
				// - neither.
				doh.register("a11yMinMaxValues",
					[
						function minAndMax(){
							var spinner = dijit.byId("integerspinner2");
							doh.isNot(spinner, null, "can't find 'integerspinner2'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
						},
						function minOnly(){
							var spinner = dijit.byId("spinnerMinOnly");
							doh.isNot(spinner, null, "can't find 'spinnerMinOnly'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							if(spinner.constraints.max){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemax"), spinner.id + ": aria-valuemax");
							}
						}, 
						function maxOnly(){
							var spinner = dijit.byId("integertextbox3");
							doh.isNot(spinner, null, "can't find 'integertextbox3'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							if(spinner.constraints.min){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemin"), spinner.id + ": aria-valuemin");
							}
						}, 
						function neitherMinNorMax(){
							var spinner = dijit.byId("integertextbox3");
							doh.isNot(spinner, null, "can't find 'integertextbox3'");
							if(spinner.constraints.min){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemin"), spinner.id + ": aria-valuemin");
							}
							if(spinner.constraints.max){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemax"), spinner.id + ": aria-valuemax");
							}
						}
					]
				);
				
				// Loop to test each spinner for aria-valuenow.
				// TODO:
				//		Is testing one spinner sufficient?
				//
				for(i=0; i<spinnerIds.length; i++){
					// test aria valuemin, valuemax, valuenow.
					doh.register("a11yValueNow", {
						name:spinnerIds[i]+"_value",
						spinnerId:spinnerIds[i],
						runTest:function(){
							var spinner = dijit.byId(this.spinnerId);
							var min = -9999;
							var max = 9999;
							if(spinner.constraints.min){
								min = spinner.constraints.min;
							}
							if(spinner.constraints.max){
								max = spinner.constraints.max;
							}
							// set spinner to some random value in its range.
							var setVal = Math.floor((Math.random() * (max - min)) + min);
							spinner.attr('value', setVal);
							var ariaVal = dijit.getWaiState(spinner.focusNode, "valuenow");
							var spinVal = spinner.attr('value');
							if(dojo.isIE >= 8) {
								// conversion to exponential with 14 places for IE8beta set/getAttribute(). see #8796.
								ariaVal = (Number(ariaVal)).toExponential(14);
								spinVal = (Number(spinVal)).toExponential(14);
							}
							doh.is(ariaVal, spinVal, spinner.id + ": aria-valuenow");
						}
					});

					
					// test "null" aria-valuenow
/* Remove null aria-valuenow test until #7866 is resolved
					doh.register("a11yNullValuenow", {
						name:spinnerIds[i]+"_nullValueNow",
						spinnerId:spinnerIds[i],
						runTest:function(){
							var spinner = dijit.byId(this.spinnerId);
							var initVal = spinner.attr('value');
							spinner.attr('value', null);
							var spinnerVal = spinner.attr('value');
							var nowVal = dijit.getWaiState(spinner.focusNode, "valuenow");
							var invalid = dijit.getWaiState(spinner.focusNode, "invalid");
							spinner.attr('value', initVal);
							doh.t(invalid, spinner.id + ": aria-invalid");
							if(!(isNaN(spinnerVal) && isNaN(nowVal))){
								doh.is(spinnerVal, nowVal, spinner.id + ": null aria-valuenow");
							}
						}
					});
*/
				}	// end for(i=0; i<spinnerIds.length, i++)
	
				// a11y tab focus tests (robot)
				doh.register("a11yTabFocus",
				[
					{
						name:"integerspinner1TabFocus",
						timeout:4000,
						setUp:function(){
							tabFocusSetup("integerspinner1");
						},
						runTest:function(){
							return a11yTabFocus("integerspinner1");
						}
					},
					{
						name:"integerspinner2TabFocus",
						timeout:4000,
						setUp:function(){
							tabFocusSetup("integerspinner2");
						},
						runTest:function(){
							return a11yTabFocus("integerspinner2");
						}
					},
					{
						name:"integertextbox3TabFocus",
						timeout:4000,
						setUp:function(){
							tabFocusSetup("integertextbox3");
						},
						runTest:function(){
							return a11yTabFocus("integertextbox3");
						}
					}/*,
					// this test fails for since there is nothing to focus on below 'realspinner1'
					{
						name:"realspinner1TabFocus",
						timeout:4000,
						setUp:function(){
							tabFocusSetup("realspinner1");
						},
						runTest:function(){
							return a11yTabFocus("realspinner1");
						}
					}*/
				]);
				
				// a11y keystroke tests
				doh.register("a11yKeystrokes",
				[
					{
						name:"integerspinner1Keystrokes",
						timeout:4000,
						setUp:function(){
							keyStrokeSetup("integerspinner1");
						},
						runTest:function(){
							return a11yKeystrokeTest("integerspinner1");
						}
					},
					{
						name:"integerspinner2Keystrokes",
						timeout:4000,
						setUp:function(){
							keyStrokeSetup("integerspinner2");
						},
						runTest:function(){
							return a11yKeystrokeTest("integerspinner2");
						}
					},
					{
						name:"integertextbox3Keystrokes",
						timeout:4000,
						setUp:function(){
							keyStrokeSetup("integertextbox3");
						},
						runTest:function(){
							return a11yKeystrokeTest("integertextbox3");
						}
					},
					{
						name:"realspinner1Keystrokes",
						timeout:4000,
						setUp:function(){
							keyStrokeSetup("realspinner1");
						},
						runTest:function(){
							return a11yKeystrokeTest("realspinner1");
						}
					}
				]);
				
				doh.run();
			});
		</script>
	</head>
</html>
